package com.neusoft.neuiotms.dcom.service.impl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.persistence.EntityNotFoundException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.neusoft.bizcore.web.constant.ExceptionConstants;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.support.Searchable;
import com.neusoft.neuiotms.dcom.converter.DutyArrangementResultConverter;
import com.neusoft.neuiotms.dcom.dto.DutyArrangementResultDTO;
import com.neusoft.neuiotms.dcom.model.DutyArrangement;
import com.neusoft.neuiotms.dcom.model.DutyArrangementGroup;
import com.neusoft.neuiotms.dcom.model.DutyArrangementResult;
import com.neusoft.neuiotms.dcom.model.Holiday;
import com.neusoft.neuiotms.dcom.model.SchedulingRuleItem;
import com.neusoft.neuiotms.dcom.repository.DutyArrangementRepository;
import com.neusoft.neuiotms.dcom.repository.DutyArrangementResultRepository;
import com.neusoft.neuiotms.dcom.repository.HolidayRepository;
import com.neusoft.neuiotms.dcom.repository.TeamGroupRepository;
import com.neusoft.neuiotms.dcom.service.DutyArrangementResultService;

@Service
public class DutyArrangementResultServiceImpl implements DutyArrangementResultService {

    @Autowired
    private DutyArrangementResultRepository dutyArrangementResultRepository;
    @Autowired
    private DutyArrangementResultConverter dutyArrangementResultConverter;
    @Autowired
    private DutyArrangementRepository dutyArrangementRepository;
    @Autowired
    private HolidayRepository holidayRepository;
    @Autowired
    private TeamGroupRepository teamGroupRepository;

    @Override
    public List<DutyArrangementResultDTO> index(final Searchable searchable) {
        final List<DutyArrangementResult> result = this.dutyArrangementResultRepository.index(searchable);
        return this.dutyArrangementResultConverter.toListDTO(result);
    }

    @Override
    public Page<DutyArrangementResultDTO> search(final Pageable pageable, final Searchable searchable) {
        final Page<DutyArrangementResult> result = this.dutyArrangementResultRepository.search(pageable, searchable);
        return this.dutyArrangementResultConverter.toPageDTO(result);
    }

    @Override
    public List<DutyArrangementResultDTO> createResults(final Long dutyArrangementId) {

        DutyArrangement model;

        try {
            model = this.dutyArrangementRepository.getOne(dutyArrangementId);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {dutyArrangementId });
        }

        // 获取规则详细项
        final List<SchedulingRuleItem> relus = model.getSchedulingRule().getItems();

        // 获取周期信息
        final List<Integer> cyclicalNumberList =
                relus.stream().map(k -> k.getCyclicalNumber()).distinct().collect(Collectors.toList());

        final Map<Integer, List<SchedulingRuleItem>> ruleSortMap = new HashMap<>();

        // 按照周期分组
        cyclicalNumberList.forEach(k -> {
            ruleSortMap.put(k,
                    relus.stream().filter(s -> s.getCyclicalNumber() == k.intValue()).collect(Collectors.toList()));
        });

        // 实际值班小组对照
        final Map<Integer, DutyArrangementGroup> groupSortMap = new HashMap<>();
        model.getGroups().forEach(k -> {
            groupSortMap.put(k.getGroupNumber(), k);
        });

        // 获取最大周期数
        final int period = cyclicalNumberList.size();

        // 获取时间段内的所有时间
        final List<Date> allDate = this.getBetweenDate(model.getStartTime(), model.getEndTime(),
                model.getSchedulingRule().getIsIncludedHoliday());

        // 存放结果
        final List<DutyArrangementResultDTO> results = new ArrayList<>();

        // 定位
        int index = 1;

        for (final Date k : allDate) {

            // 找到规则组内顺序
            if (ruleSortMap.containsKey(new Integer(index))) {
                final List<SchedulingRuleItem> ruleGroups = ruleSortMap.get(new Integer(index));

                ruleGroups.stream().map(s -> {
                    final DutyArrangementResultDTO dto = new DutyArrangementResultDTO();
                    dto.setStartTime(this.combinationTime(k, s.getStartTime()));
                    dto.setEndTime(this.combinationTime(k, s.getEndTime()));
                    dto.setClassesCode(s.getClassesItemCode());
                    dto.setClassesName(s.getClassesItemName());
                    dto.setTeamGroupId(groupSortMap.containsKey(s.getGroupNumber())
                            ? groupSortMap.get(s.getGroupNumber()).getTeamGroup().getId() : null);
                    dto.setDutyArrangementId(model.getId());
                    return dto;
                }).forEach(results::add);
            }

            index = index + 1;
            // 大于周期的时候开始循环周期
            if (index > period) {
                index = 1;
            }
        }

        List<DutyArrangementResult> models = new ArrayList<>();
        if (!CollectionUtils.isEmpty(results)) {
            models = this.dutyArrangementResultConverter.toListModel(results);
            this.dutyArrangementResultRepository.saveAll(models);

        }

        return this.dutyArrangementResultConverter.toListDTO(models);
    }

    @Override
    public DutyArrangementResultDTO details(final Long id) {

        DutyArrangementResult model;

        try {
            model = this.dutyArrangementResultRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        return this.dutyArrangementResultConverter.toDTO(model);
    }

    @Override
    public DutyArrangementResultDTO create(final DutyArrangementResultDTO dto) {

        final DutyArrangementResult model = this.dutyArrangementResultConverter.toModel(dto);
        this.dutyArrangementResultRepository.save(model);
        return this.dutyArrangementResultConverter.toDTO(model);
    }

    @Override
    public DutyArrangementResultDTO update(final Long id, final DutyArrangementResultDTO dto) {

        DutyArrangementResult model;
        try {
            model = this.dutyArrangementResultRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        dto.setId(model.getId());
        model = this.dutyArrangementResultConverter.toModel(dto);
        this.dutyArrangementResultRepository.save(model);

        return this.dutyArrangementResultConverter.toDTO(model);
    }

    @Override
    public DutyArrangementResultDTO update(final Long id, final Date date) {

        DutyArrangementResult model;
        try {
            model = this.dutyArrangementResultRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        final Date startTime = model.getStartTime();
        model.setStartTime(this.combinationTime(date, startTime));
        final Date endTime = model.getEndTime();
        model.setEndTime(this.combinationTime(date, endTime));

        this.dutyArrangementResultRepository.save(model);

        return this.dutyArrangementResultConverter.toDTO(model);
    }

    @Override
    public void delete(final Long id) {
        this.dutyArrangementResultRepository.deleteById(id);
    }

    @Override
    public void groupOrder(final Long dutyArrangementId, final Map<Long, Long> orderMap) {

        final List<DutyArrangementResult> all = new ArrayList<>();

        final Searchable searchable = new Searchable();
        searchable.put("beforeStartTime", new Date().getTime());
        searchable.put("dutyArrangementId", dutyArrangementId);
        final List<DutyArrangementResult> groupList = this.dutyArrangementResultRepository.index(searchable);

        groupList.stream().map(k -> {
            final Long groupId = k.getTeamGroup().getId();
            if (orderMap.containsKey(groupId)) {
                k.setTeamGroup(this.teamGroupRepository.getOne(orderMap.get(groupId)));
            }
            return k;
        }).forEach(all::add);

        if (all.size() > 0) {
            this.dutyArrangementResultRepository.saveAll(all);
        }
    }

    private Date combinationTime(final Date t1, final Date t2) {

        final Calendar c1 = Calendar.getInstance();
        c1.setTime(t1);

        final Calendar c2 = Calendar.getInstance();
        c2.setTime(t2);

        c1.set(Calendar.HOUR, c2.get(Calendar.HOUR_OF_DAY));
        c1.set(Calendar.MINUTE, c2.get(Calendar.MINUTE));
        c1.set(Calendar.SECOND, c2.get(Calendar.SECOND));

        return c1.getTime();

    }

    private Date combinationTime(final Date t1, final String time) {

        final Calendar c1 = Calendar.getInstance();
        c1.setTime(t1);

        int hour = Integer.valueOf(time.substring(0, 2));
        final int minute = Integer.valueOf(time.substring(3));
        if (hour == 24) {
            c1.add(Calendar.DATE, 1);
            hour = 0;
        }

        c1.set(Calendar.HOUR, hour);
        c1.set(Calendar.MINUTE, minute);
        c1.set(Calendar.SECOND, 0);

        return c1.getTime();

    }

    private List<Date> getBetweenDate(final Date begin, final Date end, final boolean IsIncludedHoliday) {
        final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        List<Date> betweenList = new ArrayList<>();

        try {
            final Calendar startDay = Calendar.getInstance();
            startDay.setTime(begin);
            startDay.add(Calendar.DATE, -1);

            while (true) {
                startDay.add(Calendar.DATE, 1);
                final Date newDate = startDay.getTime();
                final String newend = format.format(newDate);
                betweenList.add(newDate);
                if (format.format(end).equals(newend)) {
                    break;
                }
            }

            // 是否包含节假日
            if (IsIncludedHoliday) {

                final Searchable searchable = new Searchable();
                searchable.put("startTime", begin.getTime());
                searchable.put("endTime", end.getTime());
                // 去掉节假日日期
                final List<Holiday> holidayList = this.holidayRepository.index(searchable);

                if (!CollectionUtils.isEmpty(holidayList)) {
                    final List<Long> filterData =
                            holidayList.stream().map(k -> k.getDate().getTime()).collect(Collectors.toList());

                    if (!CollectionUtils.isEmpty(filterData)) {

                        betweenList = betweenList.stream().filter(k -> !filterData.contains(k.getTime()))
                                .collect(Collectors.toList());
                    }
                }

            }

        } catch (final Exception e) {
            e.printStackTrace();
        }

        return betweenList;
    }

}
